Complex software, particularly software with graphical features, often comprises millions of lines of instructions, or “code.” Today, a computer programmer typically uses several types of existing software to build new software. Central to most software engineering projects is a text editor. A “text editor,” or “editor” for short, is the computerized equivalent of a typewriter. A programmer uses a keyboard to type instructions, and the editor displays the instructions as text on the computer screen. Each line of text usually represents a single instruction. These textual instructions are referred to generally as “source code.” Computer processors, though, are incapable of understanding source code directly. Thus, after entering all of the source code in an editor, a programmer uses a “compiler” to translate the source code into instructions that a computer processor can understand and execute.
As the number of instructions increase, though, so does the probability of errors occurring in the program. Programs may contain many kinds of errors, including syntax errors and logic errors. Such errors have come to be known in the art as “bugs,” and hence, the process of locating and correcting such errors is commonly referred to as “debugging.”
A syntax error in a program is analogous to a misspelled word or a grammatical mistake in a book. But unlike a person, a computer cannot guess what word the programmer intended to use. For this reason syntax errors usually, but not always, must be found and corrected, using an editor, before a program can be compiled and executed. Logical errors, on the other hand, are latent design defects that cause a computer to execute instructions in an unexpected way, or to produce unexpected results.
Logical errors usually are uncovered only through extensive testing and use of a program. Functional, or “black box,” testing remains a common technique for detecting logical errors. In this context, a black box test is a generic procedure to check that a program produces expected output when given certain input. In effect, the actual results of program execution are compared to hypothetical results. A discrepancy between the actual results and the hypothetical results generally indicates the existence of a logic error somewhere in the code.
While a black box test can detect the existence of an error, such a test cannot identify the location of the error. And as most software engineers are painfully aware, most of the time and effort of debugging goes into locating errors, not detecting or fixing them. B. Hailpern & P. Santhanam, Software debugging, testing, and verification, 41 IBM Sys. J. 4, 8 (2002) (incorporated herein by reference). Some errors are so elusive that it may take a programmer many months, if not years, to find them. Adam Kolawa, The Evolution of Software Debugging, at http://www.parasoft.com/jsp/products/article.jsp?articleId=490 (last visited Nov. 4, 2004) (incorporated herein by reference).
A debugging technique that programmers developed relatively early in the evolution of software engineering is to insert “print statements” into the source code, which cause a program to display the value of key variables at different steps in the program's execution. Since the programmer knows where to find the print statements in the code, the programmer can narrow the location of code that produces unexpected values for these key variables. See, e.g, id.; Hailpern & Santhanam, supra at 8.
Although print statements have proven useful over the years, and continue to be used widely today, the technique is fairly crude and requires considerable time and effort on the part of the programmer. Id. Specialized debugging programs, colloquially referred to as “debuggers,” have evolved over the years to aid programmers in this difficult debugging process. Early incarnations of debuggers generally allowed a programmer to execute one instruction at a time and examine the value of any variable in the program—all without extra print statements in the source code. See Kolawa, supra. A debugger that allows a programmer to step through a program in this manner is commonly referred to as a “runtime debugger.” As Kolawa notes, “[i]n principle, a runtime debugger is nothing more than an automatic print statement.” Id. Today, most (if not all) compilers include such a runtime debugger. Id. Many debuggers also are marketed as part of an integrated development environment (IDE). An IDE typically includes, at minimum, an editor, a compiler, and a runtime debugger. Hailpern & Santhanam, supra at 8. An IDE debugger generally checks for syntax errors as the programmer develops the code in the editor, without any need to compile the program. Id.
Automated source code analysis (also sometimes referred to as “static testing”) is another “well-developed” technique for detecting bugs. Id. at 9. A source code analyzer evaluates a program's source code to identify a particular class of bugs, which the analyzer flags so that the programmer can investigate and correct as needed. Id. A programmer can run such an analyzer before attempting to compile the source code. Id. Moreover, some types of bugs, such as memory leaks, are more readily identified through analysis than testing. Id.
But even with today's high-level languages and sophisticated development environments, errors in syntax and logic remain inevitable—and expensive. Tracking down the location of a single bug in millions of lines of code can be a very time consuming process. In fact, the cost of debugging and testing a typical program today continues to run as high as 50% to 75% of the total development cost. Id. See also Kolawa, supra (debugging consumes 60-70% of development time and is responsible for 80% of all budget overruns).
Thus, there remains a need in the art for an improved software development and testing tool that reduces the time and effort of debugging software. The invention described in detail herein addresses this need. In particular it is an object of this invention to provide a debugging tool that combines the benefits of both a runtime debugger and a source code analyzer, and allows a programmer to identify bugs dynamically—as they are created. This and other objects of the invention will be apparent to those skilled in the art from the following detailed description of a preferred embodiment of the invention.